{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Operators"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:02:56.554914Z",
     "start_time": "2019-08-21T09:02:54.249612Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister\n",
    "from qiskit import BasicAer\n",
    "from qiskit.compiler import transpile\n",
    "from qiskit.quantum_info.operators import Operator, Pauli\n",
    "from qiskit.quantum_info import process_fidelity\n",
    "\n",
    "from qiskit.extensions import RXGate, XGate, CXGate"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Operator Class\n",
    "\n",
    "The `Operator` class is used in Qiskit to represent matrix operators acting on a quantum system. It has several methods to build composite operators using tensor products of smaller operators, and to compose operators.\n",
    "\n",
    "### Creating Operators\n",
    "\n",
    "The easiest way to create an operator object is to initialize it with a matrix given as a list or a Numpy array. For example, to create a two-qubit Pauli-XX operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:02:56.572857Z",
     "start_time": "2019-08-21T09:02:56.566140Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n",
       "          [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n",
       "          [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n",
       "          [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n",
       "         input_dims=(2, 2), output_dims=(2, 2))"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "XX = Operator([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]])\n",
    "XX"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Operator Properties\n",
    "\n",
    "The operator object stores the underlying matrix, and the input and output dimension of subsystems. \n",
    "\n",
    "* `data`: To access the underlying Numpy array, we may use the `Operator.data` property.\n",
    "* `dims`: To return the total input and output dimension of the operator, we may use the `Operator.dim` property. *Note: the output is returned as a tuple* `(input_dim, output_dim)`, *which is the reverse of the shape of the underlying matrix.*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:02:56.589962Z",
     "start_time": "2019-08-21T09:02:56.585681Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n",
       "       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n",
       "       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n",
       "       [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "XX.data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:02:56.615497Z",
     "start_time": "2019-08-21T09:02:56.611146Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(4, 4)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "input_dim, output_dim = XX.dim\n",
    "input_dim, output_dim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Input and Output Dimensions\n",
    "\n",
    "The operator class also keeps track of subsystem dimensions, which can be used for composing operators together. These can be accessed using the `input_dims` and `output_dims` functions.\n",
    "\n",
    "For $2^N$ by $2^M$ operators, the input and output dimension will be automatically assumed to be M-qubit and N-qubit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:02:56.804167Z",
     "start_time": "2019-08-21T09:02:56.798857Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input dimensions: (2, 2)\n",
      "Output dimensions: (2,)\n"
     ]
    }
   ],
   "source": [
    "op = Operator(np.random.rand(2 ** 1, 2 ** 2))\n",
    "print('Input dimensions:', op.input_dims())\n",
    "print('Output dimensions:', op.output_dims())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If the input matrix is not divisible into qubit subsystems, then it will be stored as a single-qubit operator. For example, if we have a $6\\times6$ matrix:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:02:57.764881Z",
     "start_time": "2019-08-21T09:02:57.760401Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input dimensions: (6,)\n",
      "Output dimensions: (6,)\n"
     ]
    }
   ],
   "source": [
    "op = Operator(np.random.rand(6, 6))\n",
    "print('Input dimensions:', op.input_dims())\n",
    "print('Output dimensions:', op.output_dims())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The input and output dimension can also be manually specified when initializing a new operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:02:58.292849Z",
     "start_time": "2019-08-21T09:02:58.287354Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input dimensions: (4,)\n",
      "Output dimensions: (2,)\n"
     ]
    }
   ],
   "source": [
    "# Force input dimension to be (4,) rather than (2, 2)\n",
    "op = Operator(np.random.rand(2 ** 1, 2 ** 2), input_dims=[4])\n",
    "print('Input dimensions:', op.input_dims())\n",
    "print('Output dimensions:', op.output_dims())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:02:58.779572Z",
     "start_time": "2019-08-21T09:02:58.774878Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input dimensions: (2, 3)\n",
      "Output dimensions: (2, 3)\n"
     ]
    }
   ],
   "source": [
    "# Specify system is a qubit and qutrit\n",
    "op = Operator(np.random.rand(6, 6),\n",
    "              input_dims=[2, 3], output_dims=[2, 3])\n",
    "print('Input dimensions:', op.input_dims())\n",
    "print('Output dimensions:', op.output_dims())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also extract just the input or output dimensions of a subset of subsystems using the `input_dims` and `output_dims` functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:03:02.187313Z",
     "start_time": "2019-08-21T09:03:02.183719Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dimension of input system 0: (2,)\n",
      "Dimension of input system 1: (3,)\n"
     ]
    }
   ],
   "source": [
    "print('Dimension of input system 0:', op.input_dims([0]))\n",
    "print('Dimension of input system 1:', op.input_dims([1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Converting classes to Operators\n",
    "\n",
    "Several other classes in Qiskit can be directly converted to an `Operator` object using the operator initialization method. For example:\n",
    "\n",
    "* `Pauli` objects\n",
    "* `Gate` and `Instruction` objects\n",
    "* `QuantumCircuit` objects\n",
    "\n",
    "Note that the last point means we can use the `Operator` class as a unitary simulator to compute the final unitary matrix for a quantum circuit, without having to call a simulator backend. If the circuit contains any unsupported operations, an exception will be raised. Unsupported operations are: measure, reset, conditional operations, or a gate that does not have a matrix definition or decomposition in terms of gate with matrix definitions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:03:02.854419Z",
     "start_time": "2019-08-21T09:03:02.842387Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n",
       "          [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n",
       "          [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n",
       "          [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n",
       "         input_dims=(2, 2), output_dims=(2, 2))"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create an Operator from a Pauli object\n",
    "\n",
    "pauliXX = Pauli('XX')\n",
    "Operator(pauliXX)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:03:03.064145Z",
     "start_time": "2019-08-21T09:03:03.058953Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n",
       "          [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n",
       "          [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n",
       "          [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]],\n",
       "         input_dims=(2, 2), output_dims=(2, 2))"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create an Operator for a Gate object\n",
    "Operator(CXGate())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:03:03.353613Z",
     "start_time": "2019-08-21T09:03:03.345462Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[0.70710678+0.j        , 0.        -0.70710678j],\n",
       "          [0.        -0.70710678j, 0.70710678+0.j        ]],\n",
       "         input_dims=(2,), output_dims=(2,))"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create an operator from a parameterized Gate object\n",
    "Operator(RXGate(np.pi / 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:03:47.550069Z",
     "start_time": "2019-08-21T09:03:47.408126Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[ 0.70710678+0.j,  0.70710678+0.j,  0.        +0.j, ...,\n",
       "            0.        +0.j,  0.        +0.j,  0.        +0.j],\n",
       "          [ 0.        +0.j,  0.        +0.j,  0.70710678+0.j, ...,\n",
       "            0.        +0.j,  0.        +0.j,  0.        +0.j],\n",
       "          [ 0.        +0.j,  0.        +0.j,  0.        +0.j, ...,\n",
       "            0.        +0.j,  0.        +0.j,  0.        +0.j],\n",
       "          ...,\n",
       "          [ 0.        +0.j,  0.        +0.j,  0.        +0.j, ...,\n",
       "            0.        +0.j,  0.        +0.j,  0.        +0.j],\n",
       "          [ 0.        +0.j,  0.        +0.j,  0.70710678+0.j, ...,\n",
       "            0.        +0.j,  0.        +0.j,  0.        +0.j],\n",
       "          [ 0.70710678+0.j, -0.70710678+0.j,  0.        +0.j, ...,\n",
       "            0.        +0.j,  0.        +0.j,  0.        +0.j]],\n",
       "         input_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2), output_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2))"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create an operator from a QuantumCircuit object\n",
    "circ = QuantumCircuit(10)\n",
    "circ.h(0)\n",
    "for j in range(1, 10):\n",
    "    circ.cx(j-1, j)\n",
    "\n",
    "# Convert circuit to an operator by implicit unitary simulation\n",
    "Operator(circ)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using Operators in circuits\n",
    "\n",
    "Unitary `Operators` can be directly inserted into a `QuantumCircuit` using the `QuantumCircuit.append` method. This converts the `Operator` into a `UnitaryGate` object, which is added to the circuit.\n",
    "\n",
    "If the operator is not unitary, an exception will be raised. This can be checked using the `Operator.is_unitary()` function, which will return `True` if the operator is unitary and `False` otherwise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:03:49.196556Z",
     "start_time": "2019-08-21T09:03:49.161398Z"
    },
    "tags": [
     "nbsphinx-thumbnail"
    ]
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 327.252x204.68 with 1 Axes>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create an operator\n",
    "XX = Operator(Pauli('XX'))\n",
    "\n",
    "# Add to a circuit\n",
    "circ = QuantumCircuit(2, 2)\n",
    "circ.append(XX, [0, 1])\n",
    "circ.measure([0,1], [0,1])\n",
    "circ.draw('mpl')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that in the above example we initialize the operator from a `Pauli` object. However, the `Pauli` object may also be directly inserted into the circuit itself and will be converted into a sequence of single-qubit Pauli gates:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'11': 1024}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "backend = BasicAer.get_backend('qasm_simulator')\n",
    "circ = transpile(circ, backend, basis_gates=['u1','u2','u3','cx'])\n",
    "job = backend.run(circ)\n",
    "job.result().get_counts(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:12.017240Z",
     "start_time": "2019-08-21T09:04:11.989825Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\">     ┌────────────┐┌─┐   \n",
       "q_0: ┤0           ├┤M├───\n",
       "     │  Pauli(XX) │└╥┘┌─┐\n",
       "q_1: ┤1           ├─╫─┤M├\n",
       "     └────────────┘ ║ └╥┘\n",
       "c: 2/═══════════════╩══╩═\n",
       "                    0  1 </pre>"
      ],
      "text/plain": [
       "     ┌────────────┐┌─┐   \n",
       "q_0: ┤0           ├┤M├───\n",
       "     │  Pauli(XX) │└╥┘┌─┐\n",
       "q_1: ┤1           ├─╫─┤M├\n",
       "     └────────────┘ ║ └╥┘\n",
       "c: 2/═══════════════╩══╩═\n",
       "                    0  1 "
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Add to a circuit\n",
    "circ2 = QuantumCircuit(2, 2)\n",
    "circ2.append(Pauli('XX'), [0, 1])\n",
    "circ2.measure([0,1], [0,1])\n",
    "circ2.draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Combining Operators\n",
    "\n",
    "Operators may be combined using several methods. \n",
    "\n",
    "### Tensor Product\n",
    "\n",
    "Two operators $A$ and $B$ may be combined into a tensor product operator $A\\otimes B$ using the `Operator.tensor` function. Note that if both $A$ and $B$ are single-qubit operators, then `A.tensor(B)` = $A\\otimes B$ will have the subsystems indexed as matrix $B$  on subsystem 0, and matrix $A$ on subsystem 1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:14.208734Z",
     "start_time": "2019-08-21T09:04:14.201058Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[ 0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j],\n",
       "          [ 0.+0.j, -0.+0.j,  0.+0.j, -1.+0.j],\n",
       "          [ 1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],\n",
       "          [ 0.+0.j, -1.+0.j,  0.+0.j, -0.+0.j]],\n",
       "         input_dims=(2, 2), output_dims=(2, 2))"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = Operator(Pauli('X'))\n",
    "B = Operator(Pauli('Z'))\n",
    "A.tensor(B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Tensor Expansion\n",
    "\n",
    "A closely related operation is `Operator.expand`, which acts like a tensor product but in the reverse order. Hence, for two operators $A$ and $B$ we have `A.expand(B)` = $B\\otimes A$ where the subsystems indexed as matrix $A$ on subsystem 0, and matrix $B$ on subsystem 1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:14.899024Z",
     "start_time": "2019-08-21T09:04:14.891072Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[ 0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j],\n",
       "          [ 1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],\n",
       "          [ 0.+0.j,  0.+0.j, -0.+0.j, -1.+0.j],\n",
       "          [ 0.+0.j,  0.+0.j, -1.+0.j, -0.+0.j]],\n",
       "         input_dims=(2, 2), output_dims=(2, 2))"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = Operator(Pauli('X'))\n",
    "B = Operator(Pauli('Z'))\n",
    "A.expand(B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Composition\n",
    "\n",
    "We can also compose two operators $A$ and $B$ to implement matrix multiplication using the `Operator.compose` method. We have that `A.compose(B)` returns the operator with matrix $B.A$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:15.655155Z",
     "start_time": "2019-08-21T09:04:15.648295Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[ 0.+0.j,  1.+0.j],\n",
       "          [-1.+0.j,  0.+0.j]],\n",
       "         input_dims=(2,), output_dims=(2,))"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = Operator(Pauli('X'))\n",
    "B = Operator(Pauli('Z'))\n",
    "A.compose(B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also compose in the reverse order by applying $B$ in front of $A$ using the `front` kwarg of `compose`:  `A.compose(B, front=True)` = $A.B$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:16.460560Z",
     "start_time": "2019-08-21T09:04:16.452319Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[ 0.+0.j, -1.+0.j],\n",
       "          [ 1.+0.j,  0.+0.j]],\n",
       "         input_dims=(2,), output_dims=(2,))"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = Operator(Pauli('X'))\n",
    "B = Operator(Pauli('Z'))\n",
    "A.compose(B, front=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Subsystem Composition\n",
    "\n",
    "Note that the previous compose requires that the total output dimension of the first operator $A$ is equal to total input dimension of the composed operator $B$ (and similarly, the output dimension of $B$ must be equal to the input dimension of $A$ when composing with `front=True`).\n",
    "\n",
    "We can also compose a smaller operator with a selection of subsystems on a larger operator using the `qargs` kwarg of `compose`, either with or without `front=True`. In this case, the relevant input and output dimensions of the subsystems being composed must match. *Note that the smaller operator must always be the argument of* `compose` *method.*\n",
    "\n",
    "For example, to compose a two-qubit gate with a three-qubit Operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:17.113510Z",
     "start_time": "2019-08-21T09:04:17.105398Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,\n",
       "            0.+0.j],\n",
       "          [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j, -1.+0.j,  0.+0.j,\n",
       "            0.+0.j],\n",
       "          [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,\n",
       "            0.+0.j],\n",
       "          [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\n",
       "           -1.+0.j],\n",
       "          [ 1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\n",
       "            0.+0.j],\n",
       "          [ 0.+0.j, -1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\n",
       "            0.+0.j],\n",
       "          [ 0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\n",
       "            0.+0.j],\n",
       "          [ 0.+0.j,  0.+0.j,  0.+0.j, -1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\n",
       "            0.+0.j]],\n",
       "         input_dims=(2, 2, 2), output_dims=(2, 2, 2))"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Compose XZ with a 3-qubit identity operator\n",
    "op = Operator(np.eye(2 ** 3))\n",
    "XZ = Operator(Pauli('XZ'))\n",
    "op.compose(XZ, qargs=[0, 2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:17.324353Z",
     "start_time": "2019-08-21T09:04:17.315952Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j],\n",
       "          [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j, 0.+0.j],\n",
       "          [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],\n",
       "          [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j],\n",
       "          [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n",
       "          [0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n",
       "          [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n",
       "          [0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n",
       "         input_dims=(2, 2, 2), output_dims=(2, 2, 2))"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Compose YX in front of the previous operator\n",
    "op = Operator(np.eye(2 ** 3))\n",
    "YX = Operator(Pauli('YX'))\n",
    "op.compose(YX, qargs=[0, 2], front=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Linear combinations\n",
    "\n",
    "Operators may also be combined using standard linear operators for addition, subtraction and scalar multiplication by complex numbers. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:18.829988Z",
     "start_time": "2019-08-21T09:04:18.812834Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[-1.5+0.j,  0. +0.j,  0. +0.j,  0. +0.j],\n",
       "          [ 0. +0.j,  1.5+0.j,  1. +0.j,  0. +0.j],\n",
       "          [ 0. +0.j,  1. +0.j,  1.5+0.j,  0. +0.j],\n",
       "          [ 0. +0.j,  0. +0.j,  0. +0.j, -1.5+0.j]],\n",
       "         input_dims=(2, 2), output_dims=(2, 2))"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "XX = Operator(Pauli('XX'))\n",
    "YY = Operator(Pauli('YY'))\n",
    "ZZ = Operator(Pauli('ZZ'))\n",
    "\n",
    "op = 0.5 * (XX + YY - 3 * ZZ)\n",
    "op"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "An important point is that while `tensor`, `expand` and `compose` will preserve the unitarity of unitary operators, linear combinations will not; hence, adding two unitary operators will, in general, result in a non-unitary operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:19.151814Z",
     "start_time": "2019-08-21T09:04:19.147497Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "op.is_unitary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Implicit Conversion to Operators\n",
    "\n",
    "Note that for all the following methods, if the second object is not already an `Operator` object, it will be implicitly converted into one by the method. This means that matrices can be passed in directly without being explicitly converted to an `Operator` first. If the conversion is not possible, an exception will be raised."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:20.045005Z",
     "start_time": "2019-08-21T09:04:20.039841Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Operator([[0.+0.j, 1.+0.j],\n",
       "          [1.+0.j, 0.+0.j]],\n",
       "         input_dims=(2,), output_dims=(2,))"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Compose with a matrix passed as a list\n",
    "Operator(np.eye(2)).compose([[0, 1], [1, 0]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comparison of Operators\n",
    "\n",
    "Operators implement an equality method that can be used to check if two operators are approximately equal. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:20.821642Z",
     "start_time": "2019-08-21T09:04:20.815611Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Operator(Pauli('X')) == Operator(XGate())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that this checks that each matrix element of the operators is approximately equal; two unitaries that differ by a global phase will not be considered equal:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:21.146256Z",
     "start_time": "2019-08-21T09:04:21.141242Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Process Fidelity\n",
    "\n",
    "We may also compare operators using the `process_fidelity` function from the *Quantum Information* module. This is an information theoretic quantity for how close two quantum channels are to each other, and in the case of unitary operators it does not depend on global phase."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:22.171481Z",
     "start_time": "2019-08-21T09:04:22.147477Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Process fidelity = 1.0\n"
     ]
    }
   ],
   "source": [
    "# Two operators which differ only by phase\n",
    "op_a = Operator(XGate()) \n",
    "op_b = np.exp(1j * 0.5) * Operator(XGate())\n",
    "\n",
    "# Compute process fidelity\n",
    "F = process_fidelity(op_a, op_b)\n",
    "print('Process fidelity =', F)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that process fidelity is generally only a valid measure of closeness if the input operators are unitary (or CP in the case of quantum channels), and an exception will be raised if the inputs are not CP."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-21T09:04:44.743744Z",
     "start_time": "2019-08-21T09:04:44.734826Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<h3>Version Information</h3><table><tr><th>Qiskit Software</th><th>Version</th></tr><tr><td><code>qiskit-terra</code></td><td>0.20.2</td></tr><tr><td><code>qiskit-aer</code></td><td>0.10.4</td></tr><tr><td><code>qiskit-ignis</code></td><td>0.7.1</td></tr><tr><td><code>qiskit-ibmq-provider</code></td><td>0.19.1</td></tr><tr><td><code>qiskit</code></td><td>0.36.2</td></tr><tr><th>System information</th></tr><tr><td>Python version</td><td>3.9.9</td></tr><tr><td>Python compiler</td><td>GCC 11.1.0</td></tr><tr><td>Python build</td><td>main, Dec 29 2021 22:19:36</td></tr><tr><td>OS</td><td>Linux</td></tr><tr><td>CPUs</td><td>32</td></tr><tr><td>Memory (Gb)</td><td>125.64821243286133</td></tr><tr><td colspan='2'>Wed Jun 22 15:52:11 2022 EDT</td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div style='width: 100%; background-color:#d5d9e0;padding-left: 10px; padding-bottom: 10px; padding-right: 10px; padding-top: 5px'><h3>This code is a part of Qiskit</h3><p>&copy; Copyright IBM 2017, 2022.</p><p>This code is licensed under the Apache License, Version 2.0. You may<br>obtain a copy of this license in the LICENSE.txt file in the root directory<br> of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.<p>Any modifications or derivative works of this code must retain this<br>copyright notice, and modified files need to carry a notice indicating<br>that they have been altered from the originals.</p></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import qiskit.tools.jupyter\n",
    "%qiskit_version_table\n",
    "%qiskit_copyright"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.9"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
